package com.think.admin.web.service.impl;

import com.think.admin.annotations.MenuAnnotation;
import com.think.admin.annotations.ResourceAnnotation;
import com.think.admin.cons.AdminCons;
import com.think.admin.model.dto.ResourceDTO;
import com.think.admin.model.param.resource.ResourceParam;
import com.think.admin.model.param.resource.ResourceUpdateParentIdParam;
import com.think.admin.model.query.ResourceQuery;
import com.think.admin.web.mapper.ResourceMapper;
import com.think.admin.web.service.IResourceService;
import com.think.admin.web.service.IRoleResourceService;
import com.think.common.enums.StateEnum;
import com.think.common.exception.SysException;
import com.think.common.param.UpdateBatchParam;
import com.think.common.utils.ResultUtil;
import com.think.common.utils.SysUtils;
import com.think.common.web.service.impl.SuperServiceImpl;
import com.think.dao.entity.Resource;
import com.think.dao.entity.RoleResource;
import com.think.dao.enums.ResourcePermissionTypeEnum;
import com.think.dao.enums.ResourceTypeEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 资源表 服务实现类
 * </p>
 *
 * @author xwj
 * @since 2019-04-08
 */
@Service
@Transactional(rollbackFor = SysException.class)
@RequiredArgsConstructor
public class ResourceServiceImpl extends SuperServiceImpl<ResourceMapper, Resource> implements IResourceService {
    private final IRoleResourceService roleResourceService;
    private final RequestMappingHandlerMapping handlerMapping;

    @Override
    public HttpEntity selectResource(ResourceQuery resourceQuery) {
        return ResultUtil.success(baseMapper.selectResource(resourceQuery));
    }

    @Override
    public HttpEntity createOrUpdate(ResourceParam resourceParam) {
        // 提取id
        String id = resourceParam.getId();

        // 不能选择本身作为上级菜单
        if (SysUtils.isNotEmpty(resourceParam.getParentId()) && resourceParam.getParentId().equals(id)) {
            throw new SysException("不能选择本身作为上级菜单");
        }

        // 权限表达式唯一
        if (SysUtils.isNotEmpty(resourceParam.getPermission())) {
            if (0 != this.lambdaQuery().eq(Resource::getPermission, resourceParam.getPermission())
                    .ne(SysUtils.isNotEmpty(id), Resource::getId, id).count()) {
                throw new SysException("权限表达式已存在");
            }
        }

        // 根据类型校验非空，并进行空值设置
        if (ResourceTypeEnum.DIRECTORY.equals(resourceParam.getType())) {
            if (SysUtils.isNull(resourceParam.getIcon())) {
                return ResultUtil.requestParamError();
            }
            resourceParam.setPermission("");
        } else if (ResourceTypeEnum.MENU.equals(resourceParam.getType())) {
            if (SysUtils.isNull(resourceParam.getUri())) {
                return ResultUtil.requestParamError();
            }
            resourceParam.setIcon("");
            resourceParam.setPermission("");
        } else if (ResourceTypeEnum.RESOURCE.equals(resourceParam.getType())) {
            if (SysUtils.isNull(resourceParam.getPermission())) {
                return ResultUtil.requestParamError();
            }
            resourceParam.setIcon("");
            resourceParam.setUri("");
        }

        // resourceParam 转换为 resource
        Resource resource = resourceParam.convert(Resource.class);
        if (SysUtils.isEmpty(resource.getParentId())) {
            resource.setParentId(AdminCons.Param.DEFAULT_RESOURCE_PARENT_ID);
        }

        // 添加
        if (this.saveOrUpdate(resource)) {
            // 重建角色资源缓存
            roleResourceService.resetRoleResource();
            return ResultUtil.success();
        } else if (SysUtils.isNotEmpty(resource.getId())) {
            return ResultUtil.updateError();
        } else {
            return ResultUtil.createError();
        }
    }

    @Override
    public HttpEntity delete(List<String> idList) {
        // 非空判断
        if (SysUtils.isEmpty(idList)) {
            return ResultUtil.requestParamError();
        }

        // 批量删除资源
        boolean pDeleteSuccess = this.lambdaUpdate().in(Resource::getId, idList).remove();
        if (pDeleteSuccess) {
            // 删除资源相关联的角色资源
            roleResourceService.lambdaUpdate().in(RoleResource::getResourceId, idList).remove();

            // 删除子资源
            List<String> childrenResourceIdList = recursiveSelectChildrenResource(idList);
            if(SysUtils.isNotEmpty(childrenResourceIdList)){
                this.lambdaUpdate().in(Resource::getId, childrenResourceIdList).remove();
                roleResourceService.lambdaUpdate().in(RoleResource::getResourceId, childrenResourceIdList).remove();
            }

            // 重建角色资源缓存
            roleResourceService.resetRoleResource();

            // 返回
            return ResultUtil.deleteSuccess();
        } else {
            throw new SysException("删除失败");
        }
    }

    @Override
    public HttpEntity updateState(UpdateBatchParam updateBatchParam) {
        // 非空判断
        if (SysUtils.isEmpty(updateBatchParam)) {
            return ResultUtil.requestParamError();
        }

        // 参数提取，非空判断
        StateEnum state = updateBatchParam.getState();
        List<String> idList = updateBatchParam.getIdList();
        if (SysUtils.isEmpty(state) || SysUtils.isEmpty(idList)) {
            return ResultUtil.requestParamError();
        }

        // 更新
        boolean updateIsSuccess = this.lambdaUpdate()
                .set(Resource::getState, state)
                .set(Resource::getGmtModified, LocalDateTime.now())
                .in(Resource::getId, idList).update();
        if (updateIsSuccess) {
            // 重建角色资源缓存
            roleResourceService.resetRoleResource();
            return ResultUtil.success();
        } else {
            return ResultUtil.updateError();
        }
    }

    @Override
    public HttpEntity refreshResource() {
        // 扫描代码中声明的资源信息
        handlerMapping.getHandlerMethods().values().forEach(handlerMethod -> setResources(handlerMethod));

        // 返回
        return ResultUtil.success();
    }

    @Override
    public HttpEntity updateResourceParent(ResourceUpdateParentIdParam resourceUpdateParentIdParam) {
        // 非空判断
        if (SysUtils.isNull(resourceUpdateParentIdParam)
                || SysUtils.isNull(resourceUpdateParentIdParam.getParentId(), resourceUpdateParentIdParam.getIdList())) {
            return ResultUtil.requestParamError();
        }

        // 参数提取
        String parentId = resourceUpdateParentIdParam.getParentId();
        List<String> idList = resourceUpdateParentIdParam.getIdList();

        // 判断所选的父级ID不能和资源ID一致
        for (String id : idList) {
            if (parentId.equals(id)) {
                throw new SysException("父资源ID不能和所选的资源ID相同");
            }
        }

        // 修改
        boolean updateIsSuccess = this.lambdaUpdate().in(Resource::getId, idList)
                .update(new Resource().setParentId(parentId));
        if (updateIsSuccess) {
            // 重建角色资源缓存
            roleResourceService.resetRoleResource();
            return ResultUtil.success();
        } else {
            return ResultUtil.updateError();
        }
    }

    @Override
    public HttpEntity getResource(String id) {
        if (SysUtils.isEmpty(id)) {
            return ResultUtil.requestParamError();
        }
        ResourceDTO resourceDTO = this.baseMapper.getResourceById(id);
        if (SysUtils.isEmpty(resourceDTO)) {
            return ResultUtil.getError();
        }
        return ResultUtil.success(resourceDTO);
    }

    /**
     * refreshResource：子方法
     *
     * @param handlerMethod
     * @return void
     * @author xwj
     * @data 2019/5/3
     */
    private void setResources(HandlerMethod handlerMethod) {
        if (SysUtils.isEmpty(handlerMethod)) {
            return;
        }

        // 处理菜单注解
        MenuAnnotation menuAnnotation = handlerMethod.getMethod().getDeclaringClass().getAnnotation(MenuAnnotation.class);
        Resource menuResource = null;
        if (SysUtils.isNotNull(menuAnnotation)) {
            String menuName = menuAnnotation.name();
            ResourcePermissionTypeEnum menuResourcePermissionType = menuAnnotation.permissionType();
            int sort = menuAnnotation.sort();
            List<Resource> menuResourceList = this.lambdaQuery().eq(Resource::getName, menuName).list();
            if (SysUtils.isEmpty(menuResourceList)) {
                menuResource = new Resource();
                menuResource.setParentId(AdminCons.Param.DEFAULT_RESOURCE_PARENT_ID);
                menuResource.setName(menuName);
                menuResource.setType(ResourceTypeEnum.MENU);
                menuResource.setPermissionType(menuResourcePermissionType);
                menuResource.setSort(sort);
                this.save(menuResource);
            } else {
                menuResource = menuResourceList.get(0);
            }
        }

        // 处理资源
        RequestMapping requestMapping = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RequestMapping.class);
        ResourceAnnotation resourceAnnotation = handlerMethod.getMethodAnnotation(ResourceAnnotation.class);
        if (SysUtils.isNull(requestMapping, resourceAnnotation) || SysUtils.isNull(requestMapping.value())) {
            return;
        }

        // 提取参数
        String pathPrefix = Arrays.stream(requestMapping.value()).collect(Collectors.joining());
        String name = resourceAnnotation.name();
        ResourcePermissionTypeEnum resourcePermissionType = resourceAnnotation.permissionType();
        int sort = resourceAnnotation.sort();

        // 构造权限表达式
        StringBuilder permission = new StringBuilder();
        GetMapping getMapping = handlerMethod.getMethodAnnotation(GetMapping.class);
        PostMapping postMapping = handlerMethod.getMethodAnnotation(PostMapping.class);
        PutMapping putMapping = handlerMethod.getMethodAnnotation(PutMapping.class);
        DeleteMapping deleteMapping = handlerMethod.getMethodAnnotation(DeleteMapping.class);
        if (SysUtils.isNotEmpty(getMapping)) {
            permission.append(RequestMethod.GET + ":" + pathPrefix
                    + Arrays.stream(getMapping.value()).collect(Collectors.joining()));
        } else if (SysUtils.isNotEmpty(postMapping)) {
            permission.append(RequestMethod.POST + ":" + pathPrefix
                    + Arrays.stream(postMapping.value()).collect(Collectors.joining()));
        } else if (SysUtils.isNotEmpty(putMapping)) {
            permission.append(RequestMethod.PUT + ":" + pathPrefix
                    + Arrays.stream(putMapping.value()).collect(Collectors.joining()));
        } else if (SysUtils.isNotEmpty(deleteMapping)) {
            permission.append(RequestMethod.DELETE + ":" + pathPrefix
                    + Arrays.stream(deleteMapping.value()).collect(Collectors.joining()));
        }
        if (SysUtils.isNull(permission.toString())) {
            return;
        }

        // 判断数据库是否存在
        if (0 != this.lambdaQuery().eq(Resource::getPermission, permission.toString()).count()) {
            return;
        }

        // 新增
        Resource resource = new Resource();
        if (SysUtils.isNull(menuResource) || SysUtils.isNull(menuResource.getId())) {
            resource.setParentId(AdminCons.Param.DEFAULT_RESOURCE_PARENT_ID);
        } else {
            resource.setParentId(menuResource.getId());
        }
        resource.setName(name);
        resource.setType(ResourceTypeEnum.RESOURCE);
        resource.setPermission(permission.toString());
        resource.setPermissionType(resourcePermissionType);
        resource.setSort(sort);
        this.save(resource);
    }

    /**
     * 递归查询子资源ID
     *
     * @param idList
     * @return java.util.List<java.lang.String>
     * @author xwj
     * @data 2019/5/6
     */
    private List<String> recursiveSelectChildrenResource(List<String> idList){
        List<String> resourceIdList = this.lambdaQuery()
                .select(Resource::getId)
                .in(Resource::getParentId, idList).list().stream()
                .map(Resource::getId).filter(SysUtils::isNotEmpty).collect(Collectors.toList());
        if(SysUtils.isNotEmpty(resourceIdList)){
            resourceIdList.addAll(recursiveSelectChildrenResource(resourceIdList));
        }
        return resourceIdList;
    }
}
